﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Resonance.TrainNode.Contracts.Entities;
using Resonance.TrainNode.Contracts.Services;
using Resonance.TrainNode.ServiceClients;

namespace Resonance.TrainNode.Test
{
	public partial class MainForm : Form
	{

		private List<string> sessionGUIDs = new List<string>();
		ITrainNodeService svcLocal;
		ITrainNodeService svcRemote1;

		public MainForm()
		{
			InitializeComponent();
			TrainNodeServiceFactory.RegisterNewNode("local", "localhost", "8000");
			TrainNodeServiceFactory.RegisterNewNode("toshiba", "192.168.1.60", "8000");
			svcLocal = TrainNodeServiceFactory.GetNodeSvc("local");
			svcRemote1 = TrainNodeServiceFactory.GetNodeSvc("toshiba");
		}

		private void AddText(string txt)
		{
			textBox1.Text = txt + "\n" + textBox1.Text;
		}

		private void timer1_Tick(object sender, EventArgs e)
		{
			if (sessionGUIDs.Count == 0)
			{
				textBox1.Text = "(No Sessions)";
				return;
			}

			StringBuilder sb = new StringBuilder();

			foreach (string sessionID in sessionGUIDs)
			{
				TrainSession session = svcLocal.GetSessionByID(sessionID);
				sb.AppendLine(string.Format("Session GUID: {0} -- Description: {2}  -- State: {1}", session.ID, session.State.ToString(), session.Description));
				sb.AppendLine();
				sb.AppendLine();

				foreach (TrainJob job in svcLocal.GetSessionJobs(session.ID))
				{
					sb.AppendLine(string.Format("Job GUID: {0}  --  State: {1} -- Epoch {2} --  Error: {3:F8}", job.ID, job.State.ToString(), job.CurrentEpoch, job.CurrentError));
				}
				sb.AppendLine();
				sb.AppendLine("**************");
				sb.AppendLine();

			}

			textBox1.Text = sb.ToString();
		}

		private void button2_Click(object sender, EventArgs e)
		{
			string sessionID = Guid.NewGuid().ToString();
			svcLocal.AddSession(sessionID, "NNet Test Session");
			sessionGUIDs.Add(sessionID);

			string nnetID = Guid.NewGuid().ToString();
			int[] nCount = new int[] { int.Parse(txtMidNeuronCount.Text), 1 };
			svcLocal.CreateNNet(sessionID, ActivationFunctionEnum.Sigmoid, 2, nnetID, 5, nCount);
			TrainParameters tp = new TrainParameters() { LearningRate = double.Parse(txtLearningRate.Text), MaxEpochs = int.Parse(txtMaxEpochs.Text), MaxError = double.Parse(txtMaxError.Text), Momentum = double.Parse(txtMomentum.Text) };

			string jobID = Guid.NewGuid().ToString();
			svcLocal.AddJobToSession(sessionID, jobID, nnetID, TrainAlgorithmTypeEnum.BackPropagation, tp);
			LoadSinusoidData(sessionID, svcLocal);
			svcLocal.StartTrainSession(sessionID);

		}

		private void LoadSinusoidData(string sessionID, ITrainNodeService trainService)
		{
			//if (openFileDialog.ShowDialog() == DialogResult.OK)
			//{
			StreamReader reader = null;
			// read maximum 50 points
			double[] data = new double[50];

			// open selected file
			reader = File.OpenText(openFileDialog.FileName);
			string str = null;
			int i = 0;

			CultureInfo culture = new CultureInfo("en-US");

			// read the data
			while ((i < 50) && ((str = reader.ReadLine()) != null))
			{
				// parse the value
				data[i] = double.Parse(str, culture.NumberFormat);

				i++;
			}
			reader.Close();

			double y_coeff = 0;
			double ymax = 0;

			for (i = 0; i < data.Length; i++)
			{
				ymax = Math.Max(ymax, Math.Abs(data[i]));
			}
			y_coeff = 1 / ymax;

			const int predictionSize = 1;
			const int windowSize = 5;

			int samples = data.Length - predictionSize - windowSize;

			for (i = 0; i < samples; i++)
			{
				TrainPattern tp = new TrainPattern();
				tp.InputValues = new double[windowSize];
				tp.OutputValues = new double[predictionSize];

				// set input
				for (int j = 0; j < windowSize; j++)
				{
					tp.InputValues[j] = data[i + j] * y_coeff;
				}
				// set output
				tp.OutputValues[0] = data[i + windowSize] * y_coeff;
				trainService.AddPatternsToSession(sessionID, new TrainPattern[] { tp });
			}
			//}
		}

		private void button3_Click(object sender, EventArgs e)
		{
			string localSessionID = Guid.NewGuid().ToString();
			svcLocal.AddSession(localSessionID, "LOCAL Session");
			string remoteSessionID = Guid.NewGuid().ToString();
			svcRemote1.AddSession(remoteSessionID, "REMOTE Session");
			LoadSinusoidData(localSessionID, svcLocal);
			LoadSinusoidData(remoteSessionID, svcRemote1);

			// Some permutations on NNet parameters...
			Dictionary<string, TrainParameters> nnetIDs = new Dictionary<string, TrainParameters>();
			for (int layers = 1; layers <= 4; layers++)
			{
				for (int hiddenLayers = 5; hiddenLayers <= 15; hiddenLayers += 3)
				{
					for (double momentum = 0.0; momentum <= 0.5; momentum += 0.05)
					{
						for (double learningRate = 0.1; learningRate <= 0.5; learningRate += 0.04)
						{
							string nnetID = Guid.NewGuid().ToString();
							int[] nCount = new int[layers + 1];
							// 1 output neuron
							nCount[layers] = 1;
							for (int n = 0; n < layers; n++)
							{
								nCount[n] = hiddenLayers;
							}
							svcLocal.CreateNNet(localSessionID, ActivationFunctionEnum.Sigmoid, 2, nnetID, 5, nCount);
							svcRemote1.CreateNNet(remoteSessionID, ActivationFunctionEnum.Sigmoid, 2, nnetID, 5, nCount);
							TrainParameters tp = new TrainParameters()
							{
								LearningRate = learningRate,
								MaxEpochs = int.Parse(txtMaxEpochs.Text),
								MaxError = double.Parse(txtMaxError.Text),
								Momentum = momentum
							};
							nnetIDs.Add(nnetID, tp);
						}
					}
				}
			}

			// Distributes jobs over train nodes
			var nKeys = nnetIDs.Keys;
			int jCount = 1;
			foreach(var net in nKeys)
			{
				TrainParameters trainParams = nnetIDs[net];

				string jobID = Guid.NewGuid().ToString();
				if ((jCount % 2) == 0)
				{
					svcLocal.AddJobToSession(localSessionID, jobID, net, TrainAlgorithmTypeEnum.BackPropagation, trainParams);
				}
				else
				{
					svcRemote1.AddJobToSession(remoteSessionID, jobID, net, TrainAlgorithmTypeEnum.BackPropagation, trainParams);
				}
				jCount++;
			}

			svcLocal.StartTrainSession(localSessionID);
			svcRemote1.StartTrainSession(remoteSessionID);
		}

		private void button4_Click(object sender, EventArgs e)
		{
			TrainSession[] sessions = svcLocal.GetSessions();

			foreach (TrainSession session in sessions)
			{
				svcLocal.StopTrainSession(session.ID);
			}

			sessions = svcRemote1.GetSessions();

			foreach (TrainSession session in sessions)
			{
				svcRemote1.StopTrainSession(session.ID);
			}
		}

		private void button5_Click(object sender, EventArgs e)
		{
			TrainSession[] sessions = svcLocal.GetSessions();

			foreach (TrainSession session in sessions)
			{
				if (session.State == SessionStateEnum.Completed || session.State == SessionStateEnum.Stopped)
				{
					sessionGUIDs.Remove(session.ID);
					svcLocal.RemoveSession(session.ID);
				}
			}
		}

		private void button6_Click(object sender, EventArgs e)
		{
			TrainSession[] sessions = svcLocal.GetSessions();

			foreach (TrainSession session in sessions)
			{
				foreach (TrainJob job in svcLocal.GetSessionJobs(session.ID))
				{
					NNet net = svcLocal.GetJobTrainResult(session.ID, job.ID);
					net.ActivationFunction = ActivationFunctionEnum.NotDefined;
				}
			}
		}

		private void btnSeries_Click(object sender, EventArgs e)
		{
			Form frm = new SeriesPredictionForm();
			frm.Show();
		}

		private void btnMgmt_Click(object sender, EventArgs e)
		{
			Form frm = new MgmtConsole();
			frm.Show();
		}

		private void btnStartRefreshing_Click(object sender, EventArgs e)
		{
			timer1.Enabled = true;
		}

		private void btnStopRefreshing_Click(object sender, EventArgs e)
		{
			timer1.Enabled = false;
		}
	}
}
